package util;

import context.*;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;

import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

// ********************************************************************
// *** CLASS: StrokeStyle
// ********************************************************************
public class StrokeStyle extends ValueType implements Context.Constants
{ //private static final boolean DEBUG=true; // Overriding Context.Constants.DEBUG
  static final String TAG="util.StrokeStyle";

  public static final Color   DEFAULT_COLOR=Color.black;
  public static final float[] DEFAULT_DASH_ARRAY=null;
  public static final float   DEFAULT_DASH_OFFSET=0.0f;
  public static final int     DEFAULT_LINE_CAP=BasicStroke.CAP_BUTT; //"butt";
  public static final int     DEFAULT_LINE_JOIN=BasicStroke.JOIN_MITER; //"miter";
  public static final float   DEFAULT_MITER_LIMIT=4.0f;
  public static final float   DEFAULT_WIDTH=1.0f;
  public static final StrokeStyle defaultStroke=new StrokeStyle(DEFAULT_COLOR,DEFAULT_WIDTH);
  
  private boolean ENABLED=true;
  private Color COLOR=DEFAULT_COLOR;
  private float[] DASH_ARRAY=DEFAULT_DASH_ARRAY;
  private float DASH_OFFSET=DEFAULT_DASH_OFFSET;
  private int LINE_CAP=DEFAULT_LINE_CAP; // BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND or BasicStroke.CAP_SQUARE
  private int LINE_JOIN=DEFAULT_LINE_JOIN; // BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL, or BasicStroke.JOIN_MITER
  private float MITER_LIMIT=DEFAULT_MITER_LIMIT;
  private float WIDTH=DEFAULT_WIDTH;
  
  public boolean getEnabled() { return(ENABLED); }
  public Color getColor() { return(COLOR); }
  public float[] getDashArray() {return(DASH_ARRAY); }
  public float getDashOffset() { return(DASH_OFFSET); }
  public int getLineCap() { return(LINE_CAP); }
  public int getLineJoin() { return(LINE_JOIN); }
  public float getMiterLimit() { return(MITER_LIMIT); }
  public float getStrokeWidth() { return(WIDTH); }
  
  public void setEnabled(boolean newEnabled)
  { if(ENABLED!=newEnabled) { ENABLED=newEnabled; FIRE_CHANGE_EVENT("ENABLED"); } }
  public void setColor(Color newColor)
  { boolean changed=false;
    if(COLOR==null) changed=(newColor!=null);
    else changed=(!COLOR.equals(newColor));
	if(changed) { COLOR=newColor; FIRE_CHANGE_EVENT("COLOR"); }
  }
  public void setDashArray(float[] newDashArray) { DASH_ARRAY=newDashArray; FIRE_CHANGE_EVENT("DASH_ARRAY"); }
  public void setDashOffset(float newDashOffset) { if(DASH_OFFSET!=newDashOffset) { DASH_OFFSET=newDashOffset; FIRE_CHANGE_EVENT("DASH_OFFSET"); } }
  public void setLineCap(int newLineCap) { if(LINE_CAP!=newLineCap) { LINE_CAP=newLineCap; FIRE_CHANGE_EVENT("LINE_CAP"); } }
  public void setLineJoin(int newLineJoin) { if(LINE_JOIN!=newLineJoin) { LINE_JOIN=newLineJoin; FIRE_CHANGE_EVENT("LINE_JOIN"); } }
  public void setMiterLimit(float newMiterLimit) { if(MITER_LIMIT!=newMiterLimit) { MITER_LIMIT=newMiterLimit; FIRE_CHANGE_EVENT("MITER_LIMIT"); } }
  public void setStrokeWidth(float newWidth) { if(WIDTH!=newWidth) { WIDTH=newWidth; FIRE_CHANGE_EVENT("WIDTH"); } }
  
  // *************************************************************************
  // *** CONSTRUCTOR: SvgStroke
  // *************************************************************************
  public StrokeStyle(Color color,float width)
  { this.COLOR=color; this.WIDTH=width; }
  
  public StrokeStyle()
  { EditorProperties.loadStroke(this); }
  
  public StrokeStyle(JSONObject parent) throws JSONException
  { JSONObject obj=parent.optJSONObject("stroke");
    if(obj!=null)
    { JSONArray clr=obj.optJSONArray("color"); if(clr!=null) setColor(Util2.decodeRGBA(clr));
      String atr=obj.optString("width"); if(atr.length()!=0) setStrokeWidth(Float.parseFloat(atr));
      atr=obj.optString("linecap"); if(atr.length()!=0) setLineCap(Util2.lineCap(atr));
      atr=obj.optString("linejoin"); if(atr.length()!=0) setLineJoin(Util2.lineJoin(atr));
      atr=obj.optString("miterlimit"); if(atr.length()!=0) setMiterLimit(Float.parseFloat(atr));
      JSONArray arr=obj.optJSONArray("dasharray"); if(arr!=null) setDashArray(Util2.decodeFloats(arr));
      atr=obj.optString("dashoffset"); if(atr.length()!=0) setDashOffset(Float.parseFloat(atr));
    }
    boolean enabled=obj.optBoolean("enabled",true);
//  setEnabled((getColor()!=null));
    setEnabled(enabled);
  }

  public JSONObject encode() throws JSONException
  {	JSONObject obj=new JSONObject();
// 	if(!getEnabled()) { obj.put("color","none"); return(obj); }
    if(!getEnabled()) obj.put("enabled",false);
	obj.put("color",Util2.encodeRGBA(getColor()));
	if(getStrokeWidth()!=DEFAULT_WIDTH) obj.put("width",getStrokeWidth());
	if(getLineCap()!=DEFAULT_LINE_CAP) obj.put("linecap",Util2.lineCap(getLineCap()));
	if(getLineJoin()!=DEFAULT_LINE_JOIN) obj.put("linejoin",Util2.lineJoin(getLineJoin()));
	if(getMiterLimit()!=DEFAULT_MITER_LIMIT) obj.put("miterlimit",getMiterLimit());
	if(getDashArray()!=DEFAULT_DASH_ARRAY) obj.put("dasharray",Util2.encodeFloats(getDashArray()));
	if(getDashOffset()!=DEFAULT_DASH_OFFSET) obj.put("dashoffset",getDashOffset());
	return(obj);
  }

  public BasicStroke getBasicStroke(int printScale)
  {	
//  float ppm=(float)PrintDialog.getPixelsPerScaledMeter(printScale);//(100); // Scale 1:100 // TODO: TEMP !!!
//	return(new BasicStroke(getStrokeWidth()/ppm,getLineCap(),getLineJoin(),getMiterLimit(),getDashArray(),getDashOffset()));
	return(new BasicStroke(getStrokeWidth()/printScale,getLineCap(),getLineJoin(),getMiterLimit(),getDashArray(),getDashOffset()));
  }

  

  public StrokeStyle copy()
  { StrokeStyle stroke=new StrokeStyle();
    copy(this,stroke);
	return(stroke);  
  }

  public static void copy(StrokeStyle from,StrokeStyle to)
  { to.setColor(from.getColor());
    to.setDashArray(from.getDashArray());
    to.setDashOffset(from.getDashOffset());
    to.setLineCap(from.getLineCap());
    to.setLineJoin(from.getLineJoin());
    to.setMiterLimit(from.getMiterLimit());
    to.setStrokeWidth(from.getStrokeWidth());
  }
  
  
  //********************************************************************
  //*** INNER CLASS: StrokeDialog
  //********************************************************************
  public class StrokeDialog extends EditValueTypeDialog 
  { static final long serialVersionUID=0; // AD'HOC
    private StrokeAttributesPanel strokeAttributesPanel;
    private StrokePreViewPanel strokePreViewPanel;
    private JButton okBtn;
    private JButton cancelBtn;
    // Initial Values:
    private StrokeStyle initialStroke;
    protected void saveInitialValues()
    { initialStroke=StrokeStyle.this.copy(); }
    protected void restoreInitialValues()
    { copy(initialStroke,StrokeStyle.this);    	  
    }

    // ******************************************************************
    // *** CONSTRUCTOR: StrokeDialog
    // ******************************************************************
    public StrokeDialog(final boolean saveToProperties)
    { super(StrokeStyle.this,"Edit Stroke Color and Attributes");
      setBounds(300,200,445,750); // Popup to: x,y,width,height
      
      final JColorChooser colorChooser=new JColorChooser(getColor());
	  final ChangeListener colorChangeListener=new ChangeListener()
	  { public void stateChanged(ChangeEvent e)
	    { setColor(colorChooser.getColor()); }
	  };
      colorChooser.getSelectionModel().addChangeListener(colorChangeListener);
      strokePreViewPanel=new StrokePreViewPanel();
	  colorChooser.setPreviewPanel(strokePreViewPanel);

      JPanel buttons=new JPanel();
      buttons.add(okBtn=new JButton("Ok"));
      okBtn.addActionListener(new ActionListener()
      { public void actionPerformed(ActionEvent e)
        { if(saveToProperties) EditorProperties.storeStroke(StrokeStyle.this);
          close();
        }
      });
      buttons.add(cancelBtn=new JButton("Cancel"));
      cancelBtn.addActionListener(new ActionListener()
      { public void actionPerformed(ActionEvent e) { cancel(); } });
      setLayout(new BorderLayout());
      strokeAttributesPanel=new StrokeAttributesPanel("Stroke");
      add(strokeAttributesPanel,BorderLayout.CENTER);//.NORTH);
      add(colorChooser, BorderLayout.NORTH);//.CENTER);
      add(buttons, BorderLayout.SOUTH);
      setAlwaysOnTop(true);
      setVisible(true);
    }
  }

  // ****************************************************************
  // *** INNER CLASS : StrokeAttributesPanel
  // ****************************************************************
  private class StrokeAttributesPanel extends JPanel
  { static final long serialVersionUID=0; // AD'HOC

    public StrokeAttributesPanel(String title)
    { setBorder(Util2.getTitleBorder(title));
      setPreferredSize(new Dimension(350,170+10));
      setLayout(new GridLayout(7,2));//(1,2));
      
      add(new JLabel("Stroke Width:"));
      add(new ValueSpinner(getStrokeWidth(),1.0,10.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setStrokeWidth((float)value); }	
      });
        
      add(new JLabel("Stroke Opacity: [0:255]"));
      add(new ValueSpinner(getColor().getAlpha(),0,255,1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setColor(Util2.alfaColor(getColor(),(int)value)); }	
      });
      
      add(new JLabel("Line Cap:"));
      { String[] vals={"butt","round","square"};
        final JComboBox comboBox=new JComboBox(vals);
        switch(getLineCap())
        { case BasicStroke.CAP_BUTT: comboBox.setSelectedIndex(0); break;
          case BasicStroke.CAP_ROUND: comboBox.setSelectedIndex(1); break;
          case BasicStroke.CAP_SQUARE: comboBox.setSelectedIndex(2); break;
        }
        comboBox.addActionListener(new ActionListener()
        { public void actionPerformed(ActionEvent e)
          { String selected=(String)comboBox.getSelectedItem();
    	    if(DEBUG) Log.d(TAG,"ACTION: "+comboBox.getSelectedItem());
            if(selected.equalsIgnoreCase("butt")) setLineCap(BasicStroke.CAP_BUTT);
            if(selected.equalsIgnoreCase("round")) setLineCap(BasicStroke.CAP_ROUND);
            if(selected.equalsIgnoreCase("square")) setLineCap(BasicStroke.CAP_SQUARE);
          }
        });
        add(comboBox);
      }
      
      add(new JLabel("Line Join:"));
      { String[] vals={"miter","round","bevel"};
        final JComboBox comboBox=new JComboBox(vals);
        switch(getLineJoin())
        { case BasicStroke.JOIN_MITER: comboBox.setSelectedIndex(0); break;
          case BasicStroke.JOIN_ROUND: comboBox.setSelectedIndex(1); break;
          case BasicStroke.JOIN_BEVEL: comboBox.setSelectedIndex(2); break;
        }
        comboBox.addActionListener(new ActionListener()
        { public void actionPerformed(ActionEvent e)
          { String selected=(String)comboBox.getSelectedItem();
            if(selected.equalsIgnoreCase("miter")) setLineJoin(BasicStroke.JOIN_MITER);
            if(selected.equalsIgnoreCase("round")) setLineJoin(BasicStroke.JOIN_ROUND);
            if(selected.equalsIgnoreCase("bevel")) setLineJoin(BasicStroke.JOIN_BEVEL);
          }
        });
        add(comboBox);
      } 
      
      add(new JLabel("Stroke Miter Limit:"));
      add(new ValueSpinner(getMiterLimit(),1.0,10.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setMiterLimit((float)value); }	
      });
  
      add(new JLabel("Dash Array:"));
      { String[] vals={"none","dash dash","dash dot"};
        final float[] dashDash={20,10};
        final float[] dashDot={10,5,2,5};
        int dashIndex=0;
        String ds=Util2.encodeFloats(getDashArray()).toString();
//      Debug.TRACE("*** DASH - ds="+ds);
//      Debug.TRACE("*** DASH - dashDash="+Util.encodeFloats(dashDash,"none"));
//      Debug.TRACE("*** DASH - dashDot="+Util.encodeFloats(dashDot,"none"));
        if(ds.equals(Util2.encodeFloats(dashDash).toString())) dashIndex=1;
        if(ds.equals(Util2.encodeFloats(dashDot).toString())) dashIndex=2;
        final JComboBox comboBox=new JComboBox(vals);
        comboBox.setSelectedIndex(dashIndex);
        comboBox.addActionListener(new ActionListener()
        { public void actionPerformed(ActionEvent e)
          { String selected=(String)comboBox.getSelectedItem();
            if(selected.equalsIgnoreCase("none")) setDashArray(null);
            if(selected.equalsIgnoreCase("dash dash")) setDashArray(dashDash);
            if(selected.equalsIgnoreCase("dash dot")) setDashArray(dashDot);
          }
        });
        add(comboBox);
      }
      
      add(new JLabel("Dash Offset:"));
      add(new ValueSpinner(getDashOffset(),0.0,10.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setDashOffset((float)value); }	
      });
    }
  }

  // ****************************************************************
  // *** INNER CLASS: StrokePreViewPanel
  // ****************************************************************
  public class StrokePreViewPanel extends JPanel
  { static final long serialVersionUID=0; // AD'HOC
    // **************************************************************
    // *** CONSTRUCTOR: StrokePreViewPanel
    // **************************************************************
    public StrokePreViewPanel()
    { setBorder(new EmptyBorder(10,5,5,5));
      setPreferredSize(new Dimension(350,200));
    }
    
    public void paint(Graphics g)
    { super.paint(g);
      int w=getWidth();
      int h=getHeight();
      g.setColor(Color.white);
      int cx=w/2;
      int cy=h/2;
      int dx=w/40;
      int dy=h/40;
      g.fillRect(10,20,w-20,h-30);
      GeneralPath path=new GeneralPath();
      path.moveTo(cx-10*dx,cy-10*dy);
      path.lineTo(cx+10*dx,cy-10*dy);
      path.moveTo(cx-10*dx,cy);
      path.lineTo(cx+5*dx,cy);
      path.lineTo(cx+5*dx,cy+10*dy);
      path.closePath();
      g.setColor(getColor());
      ((Graphics2D)g).setStroke(getBasicStroke(1));
	  ((Graphics2D)g).draw(path);    	  
    }
  }

}
